library(tidyverse)
Warning messages:
1: In system("timedatectl", intern = TRUE) :
  running command 'timedatectl' had status 1
2: In readChar(file, size, TRUE) : truncating string with embedded nuls

setup for GO term analysis

The LMM was run by Natsuhiko, see /nfs/team205/nk5/Team205/ed6/DE/run.R

ct_df <- read_tsv(list.files('/nfs/team205/nk5/Team205/ed6/DE/', pattern = "^cell", full.names = TRUE), col_names = FALSE) %>%
  rename(ID=X1, celltype=X2)

── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  X1 = col_double(),
  X2 = col_character()
)

Read results

Table provided by Natsuhiko

de_genes <- read_csv("~/mount/gdrive/Pan_fetal/significant_genes/LMM_Natsuhiko_results/de_lymphoid_ltsr0.9.csv")

── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  celltype = col_character(),
  `organ (in which the gene is up/down-regulated)` = col_character(),
  gene = col_character(),
  `ltsr (posterior prob of DE)` = col_double(),
  logFoldChange = col_double(),
  BM = col_double(),
  GU = col_double(),
  KI = col_double(),
  LI = col_double(),
  MLN = col_double(),
  SK = col_double(),
  SP = col_double(),
  TH = col_double(),
  YS = col_double()
)
colnames(de_genes) <- str_remove(colnames(de_genes), " .+")
de_genes

Save number of cells x celltype and organ

n_cells <- read_csv('/nfs/team205/nk5/Team205/ed6/metadata.csv.gz') %>%
  group_by(anno_lvl_2, organ) %>%
  summarise(n_cells=n()) %>%
  rename(celltype=anno_lvl_2)

── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  index = col_character(),
  Sample = col_character(),
  donor = col_character(),
  organ = col_character(),
  anno_lvl_2 = col_character(),
  age = col_double(),
  method = col_character()
)

`summarise()` has grouped output by 'anno_lvl_2'. You can override using the `.groups` argument.
n_cells %>%
  filter(str_detect(celltype, "ELP"))
organs <- unique(de_genes$organ)

de_genes %>%
  # filter(celltype=="MATURE_B" & organ=="YS") %>%
  # pivot_longer(cols=organs, names_to="organ_expr", values_to="expr") %>%
  mutate(rank=rank(ltsr)) %>%
  ggplot(aes(rank, ltsr)) + 
  geom_point() 

General stats

How many genes are DE in multiple celltypes per organ?

de_genes %>%
  distinct(organ, celltype, gene) %>%
  group_by(organ, gene) %>%
  summarise(n=n()) %>%
  ggplot(aes(n)) + geom_histogram() +
  facet_wrap(organ~., scales="free")
`summarise()` has grouped output by 'organ'. You can override using the `.groups` argument.

How many genes are DE in multiple organs per celltype?

de_genes %>%
  distinct(organ, celltype, gene) %>%
  group_by(celltype, gene) %>%
  summarise(n=n()) %>%
  ggplot(aes(n)) + geom_histogram() 
`summarise()` has grouped output by 'celltype'. You can override using the `.groups` argument.

Number of DE genes per pair

de_genes %>%
  group_by(organ, celltype) %>%
  summarise(n_de_genes = n()) %>%
  ggplot(aes(organ, celltype, fill=n_de_genes)) +
  geom_tile() +
  scale_fill_viridis_c()

de_genes %>%
  group_by(organ, celltype) %>%
  summarise(n_de_genes = n()) %>%
  ggplot(aes(celltype, n_de_genes)) +
  geom_col() +
  coord_flip() +
  theme_bw(base_size = 14) +
  facet_wrap(organ~., scales="free_x")
`summarise()` has grouped output by 'organ'. You can override using the `.groups` argument.

de_genes %>%
  group_by(organ, celltype) %>%
  summarise(n_de_genes = n()) %>%
  ggplot(aes(celltype, n_de_genes, fill=organ)) +
  geom_col() +
  coord_flip() +
  theme_bw(base_size = 14) +
  scale_color_brewer(palette="Spectral") +
  scale_fill_brewer(palette="Spectral")
`summarise()` has grouped output by 'organ'. You can override using the `.groups` argument.

de_genes %>%
  group_by(organ, celltype) %>%
  summarise(n_de_genes = n()) %>%
  left_join(n_cells) %>%
  ggplot(aes(log10(n_cells), n_de_genes, color=organ)) +
  geom_point() +
  scale_color_brewer(palette="Spectral")
`summarise()` has grouped output by 'organ'. You can override using the `.groups` argument.
Joining, by = c("organ", "celltype")

Overlap between celltype specific genes

for (o in organs){
  p_df <- de_genes %>%
    group_by(gene, organ) %>%
    mutate(n_celltype=n()) %>%
    ungroup() %>%
    filter(organ == o) %>%
    filter(n_celltype > 3) %>%
    # filter(abs(logFoldChange) > 0.1) %>%
    rename(org_expr = o) %>%
    arrange(- org_expr) %>%
    mutate(gene=factor(gene, levels=unique(gene))) %>%
    left_join(ct_class_df) %>%
    mutate(celltype=factor(celltype, levels=unlist(ct_order))) 
  n_genes <- length(unique(p_df$gene))
  pl_height <- ifelse(n_genes > 10, 0.3*n_genes, 1*n_genes)
  p <- p_df %>%
    ggplot(aes(celltype, gene)) +
    facet_grid(organ~class, scales="free_x", space="free") +
    geom_point(aes(fill=org_expr, size=abs(org_expr)), shape=21, color='black') +
    scale_fill_gradient2(high="red", low="blue") +
    scale_color_gradient2(high="red", low="blue") +
    theme_gray(base_size=14) +
    theme(axis.text.x=element_text(angle=90, hjust=1, vjust=0.5)) 
  print(p) 
}
Joining, by = "celltype"
Error: Faceting variables must have at least one value
Run `rlang::last_error()` to see where the error occurred.

o = "TH"

p_df <- de_genes %>%
    group_by(gene, organ) %>%
    mutate(n_celltype=n()) %>%
    ungroup() %>%
    filter(organ == o) %>%
    filter(n_celltype > 5) %>%
    # filter(abs(logFoldChange) > 0.1) %>%
    rename(org_expr = o) %>%
    arrange(- org_expr) %>%
    mutate(gene=factor(gene, levels=unique(gene))) %>%
    left_join(ct_class_df) %>%
    mutate(celltype=factor(celltype, levels=unlist(ct_order))) 
Joining, by = "celltype"
n_genes <- length(unique(p_df$gene))

p_top <- n_cells[n_cells$organ==o,] %>%
    mutate(organ=factor(organ, levels=organs)) %>%
    filter(celltype %in% p_df$celltype) %>%
    left_join(ct_class_df) %>%
    mutate(celltype=factor(celltype, levels=unlist(ct_order)))  %>%
    ggplot(aes(celltype, log10(n_cells))) +
      geom_col() +
    theme_bw(base_size=14) +
  facet_grid(organ~class, scales="free_x", space="free") +
  theme(axis.text.x=element_blank()) 
Joining, by = "celltype"
p <- p_df %>%
    ggplot(aes(celltype, gene)) +
    facet_grid(organ~class, scales="free_x", space="free") +
    geom_point(aes(fill=org_expr, size=abs(org_expr)), shape=21, color='black') +
    scale_fill_gradient2(high="red", low="blue") +
    scale_color_gradient2(high="red", low="blue") +
    theme_gray(base_size=14) +
    theme(axis.text.x=element_text(angle=90, hjust=1, vjust=0.5)) 

(p_top / p)  + plot_layout(heights=c(1,5))

NA
NA
plot_celltype_org_de(de_genes, org = "BM", ct="ELP/PRE_PRO_B", minFC = 0.5)

plot_celltype_org_de(de_genes, org = "LI", ct="ELP/PRE_PRO_B", minFC = 0.5)

plot_celltype_org_de(de_genes, org = "TH", ct="Progenitor", minFC = 1)

plot_celltype_org_de(de_genes, org = "TH", ct="DN(P)/DN(early)")

plot_celltype_org_de(de_genes, org = "TH", ct="B1")

plot_celltype_org_de(de_genes, org = "LI", ct="B1")

plot_celltype_org_de(de_genes, org = "SP", ct="B1")

plot_celltype_org_de(de_genes, org = "BM", ct="B1")

plot_celltype_org_de(de_genes, org = "SP", ct="NKT")

plot_celltype_org_de(de_genes, org = "SP", ct="NK")

Visualize results

de_genes %>%
  filter(celltype=="CD8aa") %>%
  pivot_longer(cols = organs, names_to="expr_organs", values_to="expr") %>%
  group_by(organ, expr_organs) %>%
  summarise(signature=mean(abs(expr))) %>%
  ggplot(aes(expr_organs, signature, color=organ)) + geom_point()
  
plot_celltype_org_de(de_genes, "MATURE_B", "YS") +
  coord_fixed()
Note: Using an external vector in selections is ambiguous.
ℹ Use `all_of(organs)` instead of `organs` to silence this message.
ℹ See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
This message is displayed once per session.

plot_celltype_org_de(de_genes, "CD4+T", "MLN", minFC = 0.3) +
  coord_fixed() +
plot_celltype_org_de(de_genes, "CD8+T", "MLN", minFC = 0.3) +
  coord_fixed() 
  
plot_celltype_org_de(de_genes, "CD4+T", "SK", minFC = 0.2) +
  coord_fixed() +
plot_celltype_org_de(de_genes, "CD4+T", "GU", minFC = 0.2) +
  coord_fixed() +
plot_celltype_org_de(de_genes, "CD4+T", "KI", minFC = 0.2) +
  coord_fixed() 
plot_celltype_org_de(de_genes, "CD8+T", "SP", minFC = 0.2) +
  coord_fixed() 
plot_celltype_org_de(de_genes, "MATURE_B", "TH", minFC = 0.8) +
  coord_fixed()

Read all results

dim(de_organ$beta)
[1] 12517     9
org <- "LI"
ct <- "MATURE_B"

ct_id <- ct_df$ID[ct_df$celltype==ct]

de_ct_org <- readRDS(glue('/nfs/team205/nk5/Team205/ed6/DE/de_{ct_id}.RDS'))
signif_genes <- de_genes$gene[de_genes$organ==org & de_genes$celltype==ct]

data.frame(ct=de_organ$beta[,org], ct_org = de_ct_org$beta[,glue("{ct}:{org}")], gene=rownames(de_celltype$beta)) %>%
  mutate(is_signif = gene %in% signif_genes) %>%
  ggplot(aes(ct, ct_org)) +
  geom_point() +
  geom_point(data= . %>% filter(is_signif), color="red") +
  ggrepel::geom_text_repel(data= . %>% filter(is_signif), color="red", aes(label=gene)) +
  xlab(org) + ylab(glue("{ct}:{org}"))

VS marker genes

org1 <- "BM"
org2 <- "LI"
ct <- "PRO_B"

ct_id <- ct_df$ID[ct_df$celltype==ct]

de_ct_org <- readRDS(glue('/nfs/team205/nk5/Team205/ed6/DE/de_{ct_id}.RDS'))
signif_genes <- de_genes$gene[de_genes$organ==org & de_genes$celltype==ct]

data.frame(ct=de_ct_org$beta[,glue("{ct}:{org1}")], ct_org = de_ct_org$beta[,glue("{ct}:{org2}")], gene=rownames(de_celltype$beta)) %>%
  mutate(is_signif = gene %in% signif_genes) %>%
  ggplot(aes(ct, ct_org)) +
  geom_point() +
  geom_point(data= . %>% filter(is_signif), color="red") +
  geom_text(data= . %>% filter(is_signif), color="red", aes(label=gene)) +
  xlab(glue("{ct}:{org1}")) + ylab(glue("{ct}:{org2}"))

library(Hmisc)

for (ct in ct_df$celltype){
  ct_id <- ct_df$ID[ct_df$celltype==ct]
  de_ct_org <- readRDS(glue('/nfs/team205/nk5/Team205/ed6/DE/de_{ct_id}.RDS'))
  
  cormat <- rcorr(de_ct_org$beta)  
  cormat$r[cormat$P > 0.01] <- 0
  corrplot::corrplot(cormat$r,method = 'color' , diag = TRUE, addCoef.col = "grey", hclust.method = "ward")
  
}

de_genes %>%
  group_by(organ, celltype) %>%
  summarise(n_de_genes = n()) %>%
  ggplot(aes(organ, n_de_genes)) +
  geom_col() +
  coord_flip() +
  theme_bw(base_size = 14) +
  facet_wrap(celltype~., scales="free_x")
`summarise()` has grouped output by 'organ'. You can override using the `.groups` argument.

Compare correlation between coefficients and signatures

i.e. cells where the gene set is expressed

cor(sig_mat[1:100,1:10])
                        sig_score_TH_Progenitor sig_score_TH_abT(entry) sig_score_BM_B1 sig_score_GU_B1 sig_score_LI_B1 sig_score_SP_B1
sig_score_TH_Progenitor              1.00000000               0.2607735     -0.03180426    -0.049933182     -0.38547715    -0.142414491
sig_score_TH_abT(entry)              0.26077353               1.0000000      0.12534175    -0.179732663     -0.24388387     0.147321331
sig_score_BM_B1                     -0.03180426               0.1253418      1.00000000    -0.503860150     -0.08516963     0.035257081
sig_score_GU_B1                     -0.04993318              -0.1797327     -0.50386015     1.000000000      0.29912966    -0.003256735
sig_score_LI_B1                     -0.38547715              -0.2438839     -0.08516963     0.299129659      1.00000000     0.325463255
sig_score_SP_B1                     -0.14241449               0.1473213      0.03525708    -0.003256735      0.32546326     1.000000000
sig_score_TH_B1                     -0.03911572              -0.1111063     -0.64250985     0.783402416      0.18909408    -0.003337539
sig_score_BM_CD4+T                   0.12708686              -0.2959110     -0.21383857     0.270087050      0.30057736    -0.082727222
sig_score_LI_CD4+T                  -0.32082456              -0.3293925     -0.45615206     0.656951830      0.54229874     0.222532475
sig_score_MLN_CD4+T                 -0.21302739              -0.3044921     -0.18037049     0.048376133      0.06461616     0.165906852
                        sig_score_TH_B1 sig_score_BM_CD4+T sig_score_LI_CD4+T sig_score_MLN_CD4+T
sig_score_TH_Progenitor    -0.039115717         0.12708686         -0.3208246         -0.21302739
sig_score_TH_abT(entry)    -0.111106349        -0.29591101         -0.3293925         -0.30449214
sig_score_BM_B1            -0.642509845        -0.21383857         -0.4561521         -0.18037049
sig_score_GU_B1             0.783402416         0.27008705          0.6569518          0.04837613
sig_score_LI_B1             0.189094076         0.30057736          0.5422987          0.06461616
sig_score_SP_B1            -0.003337539        -0.08272722          0.2225325          0.16590685
sig_score_TH_B1             1.000000000         0.09536250          0.6250180          0.03827978
sig_score_BM_CD4+T          0.095362501         1.00000000          0.1790338          0.07268378
sig_score_LI_CD4+T          0.625018029         0.17903375          1.0000000          0.17857203
sig_score_MLN_CD4+T         0.038279779         0.07268378          0.1785720          1.00000000

long_cor <- left_join(long_cor_sig, long_cor_beta) 
Joining, by = c("Var1", "Var2")

LS0tCnRpdGxlOiAiUGFuIEZldGFsIEltbXVuZSAtIERFIHJlc3VsdHMgYnkgTmF0c3VoaWtvIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKc2V0dXAgZm9yIEdPIHRlcm0gYW5hbHlzaXMKCmBgYHtyLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQojIEJpb2NNYW5hZ2VyOjppbnN0YWxsKCdjbHVzdGVyUHJvZmlsZXInKQojIEJpb2NNYW5hZ2VyOjppbnN0YWxsKCdtc2lnZGJyJykKbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpCmxpYnJhcnkobXNpZ2RicikKCm1fZGYgPC0gbXNpZ2RicihzcGVjaWVzID0gIkhvbW8gc2FwaWVucyIpCm1fdDJnIDwtIG1zaWdkYnIoc3BlY2llcyA9ICJIb21vIHNhcGllbnMiLCBjYXRlZ29yeSA9ICJDNSIsIHN1YmNhdGVnb3J5ID0gIkJQIikgICU+JSAKICBkcGx5cjo6c2VsZWN0KGdzX25hbWUsIGdlbmVfc3ltYm9sKQpgYGAKCgpUaGUgTE1NIHdhcyBydW4gYnkgTmF0c3VoaWtvLCBzZWUgYC9uZnMvdGVhbTIwNS9uazUvVGVhbTIwNS9lZDYvREUvcnVuLlJgCgpgYGB7cn0Kb3V0ZmlsZXMgPC0gbGlzdC5maWxlcygnL25mcy90ZWFtMjA1L25rNS9UZWFtMjA1L2VkNi9ERScsIHBhdHRlcm4gPSAiZGUiLCBmdWxsLm5hbWVzID0gVFJVRSkKCmN0X2RmIDwtIHJlYWRfdHN2KGxpc3QuZmlsZXMoJy9uZnMvdGVhbTIwNS9uazUvVGVhbTIwNS9lZDYvREUvJywgcGF0dGVybiA9ICJeY2VsbCIsIGZ1bGwubmFtZXMgPSBUUlVFKSwgY29sX25hbWVzID0gRkFMU0UpICU+JQogIHJlbmFtZShJRD1YMSwgY2VsbHR5cGU9WDIpCgojIyBBbGwgdGhlIGdlbmVzCmdlbmUgPSByZWFkLmNzdigiL25mcy90ZWFtMjA1L25rNS9UZWFtMjA1L2VkNi9nZW5lLmNzdi5neiIsYXMuaXM9VCkKIkhHSDEiICVpbiUgZ2VuZSRHZW5lTmFtZQpgYGAKCiMjIFJlYWQgcmVzdWx0cwoKVGFibGUgcHJvdmlkZWQgYnkgTmF0c3VoaWtvCgpgYGB7cn0KZGVfZ2VuZXMgPC0gcmVhZF9jc3YoIn4vbW91bnQvZ2RyaXZlL1Bhbl9mZXRhbC9zaWduaWZpY2FudF9nZW5lcy9MTU1fTmF0c3VoaWtvX3Jlc3VsdHMvZGVfbHltcGhvaWRfbHRzcjAuOS5jc3YiKQpjb2xuYW1lcyhkZV9nZW5lcykgPC0gc3RyX3JlbW92ZShjb2xuYW1lcyhkZV9nZW5lcyksICIgLisiKQpkZV9nZW5lcwpgYGAKClNhdmUgbnVtYmVyIG9mIGNlbGxzIHggY2VsbHR5cGUgYW5kIG9yZ2FuCgpgYGB7cn0Kbl9jZWxscyA8LSByZWFkX2NzdignL25mcy90ZWFtMjA1L25rNS9UZWFtMjA1L2VkNi9tZXRhZGF0YS5jc3YuZ3onKSAlPiUKICBncm91cF9ieShhbm5vX2x2bF8yLCBvcmdhbikgJT4lCiAgc3VtbWFyaXNlKG5fY2VsbHM9bigpKSAlPiUKICByZW5hbWUoY2VsbHR5cGU9YW5ub19sdmxfMikKbl9jZWxscyAlPiUKICBmaWx0ZXIoc3RyX2RldGVjdChjZWxsdHlwZSwgIkVMUCIpKQpgYGAKCgpgYGB7cn0Kb3JnYW5zIDwtIHVuaXF1ZShkZV9nZW5lcyRvcmdhbikKCmRlX2dlbmVzICU+JQogICMgZmlsdGVyKGNlbGx0eXBlPT0iTUFUVVJFX0IiICYgb3JnYW49PSJZUyIpICU+JQogICMgcGl2b3RfbG9uZ2VyKGNvbHM9b3JnYW5zLCBuYW1lc190bz0ib3JnYW5fZXhwciIsIHZhbHVlc190bz0iZXhwciIpICU+JQogIG11dGF0ZShyYW5rPXJhbmsobHRzcikpICU+JQogIGdncGxvdChhZXMocmFuaywgbHRzcikpICsgCiAgZ2VvbV9wb2ludCgpIApgYGAKCiMjIEdlbmVyYWwgc3RhdHMKCkhvdyBtYW55IGdlbmVzIGFyZSBERSBpbiBtdWx0aXBsZSBjZWxsdHlwZXMgcGVyIG9yZ2FuPwpgYGB7cn0KZGVfZ2VuZXMgJT4lCiAgZGlzdGluY3Qob3JnYW4sIGNlbGx0eXBlLCBnZW5lKSAlPiUKICBncm91cF9ieShvcmdhbiwgZ2VuZSkgJT4lCiAgc3VtbWFyaXNlKG49bigpKSAlPiUKICBnZ3Bsb3QoYWVzKG4pKSArIGdlb21faGlzdG9ncmFtKCkgKwogIGZhY2V0X3dyYXAob3JnYW5+Liwgc2NhbGVzPSJmcmVlIikKCmBgYAoKSG93IG1hbnkgZ2VuZXMgYXJlIERFIGluIG11bHRpcGxlIG9yZ2FucyBwZXIgY2VsbHR5cGU/CmBgYHtyfQpkZV9nZW5lcyAlPiUKICBkaXN0aW5jdChvcmdhbiwgY2VsbHR5cGUsIGdlbmUpICU+JQogIGdyb3VwX2J5KGNlbGx0eXBlLCBnZW5lKSAlPiUKICBzdW1tYXJpc2Uobj1uKCkpICU+JQogIGdncGxvdChhZXMobikpICsgZ2VvbV9oaXN0b2dyYW0oKSAKCmBgYAoKIyMjIE51bWJlciBvZiBERSBnZW5lcyBwZXIgcGFpcgoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CmRlX2dlbmVzICU+JQogIGdyb3VwX2J5KG9yZ2FuLCBjZWxsdHlwZSkgJT4lCiAgc3VtbWFyaXNlKG5fZGVfZ2VuZXMgPSBuKCkpICU+JQogIGdncGxvdChhZXMob3JnYW4sIGNlbGx0eXBlLCBmaWxsPW5fZGVfZ2VuZXMpKSArCiAgZ2VvbV90aWxlKCkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKCkKYGBgCmBgYHtyLCBmaWcuaGVpZ2h0PTExLCBmaWcud2lkdGg9MTF9CmRlX2dlbmVzICU+JQogIGdyb3VwX2J5KG9yZ2FuLCBjZWxsdHlwZSkgJT4lCiAgc3VtbWFyaXNlKG5fZGVfZ2VuZXMgPSBuKCkpICU+JQogIGdncGxvdChhZXMoY2VsbHR5cGUsIG5fZGVfZ2VuZXMpKSArCiAgZ2VvbV9jb2woKSArCiAgY29vcmRfZmxpcCgpICsKICB0aGVtZV9idyhiYXNlX3NpemUgPSAxNCkgKwogIGZhY2V0X3dyYXAob3JnYW5+Liwgc2NhbGVzPSJmcmVlX3giKQpgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9N30KZGVfZ2VuZXMgJT4lCiAgZ3JvdXBfYnkob3JnYW4sIGNlbGx0eXBlKSAlPiUKICBzdW1tYXJpc2Uobl9kZV9nZW5lcyA9IG4oKSkgJT4lCiAgZ2dwbG90KGFlcyhjZWxsdHlwZSwgbl9kZV9nZW5lcywgZmlsbD1vcmdhbikpICsKICBnZW9tX2NvbCgpICsKICBjb29yZF9mbGlwKCkgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE0KSArCiAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGU9IlNwZWN0cmFsIikgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGU9IlNwZWN0cmFsIikKICAKIyBmYWNldF93cmFwKG9yZ2Fufi4sIHNjYWxlcz0iZnJlZV94IikKYGBgCmBgYHtyfQpkZV9nZW5lcyAlPiUKICBncm91cF9ieShvcmdhbiwgY2VsbHR5cGUpICU+JQogIHN1bW1hcmlzZShuX2RlX2dlbmVzID0gbigpKSAlPiUKICBsZWZ0X2pvaW4obl9jZWxscykgJT4lCiAgZ2dwbG90KGFlcyhsb2cxMChuX2NlbGxzKSwgbl9kZV9nZW5lcywgY29sb3I9b3JnYW4pKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZT0iU3BlY3RyYWwiKQogICAKYGBgCgpPdmVybGFwIGJldHdlZW4gY2VsbHR5cGUgc3BlY2lmaWMgZ2VuZXMKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xNX0KY3Rfb3JkZXIgPC0gbGlzdChwcm9nZW5pdG9ycz1jKCJQcm9nZW5pdG9yIiwgIk1QUCIpLAogICAgICAgICAgICAgICAgIEJfY2VsbCA9IGMoIkVMUC9QUkVfUFJPX0IiLCAiRUFSTFlfUFJPX0IiLCJQUk9fQiIsICJMQVRFX1BST19CIiwnUFJPX1RPX1BSRV9CJywgIkxBUkdFX1BSRV9CIiwgIlNNQUxMX1BSRV9CIiwgIklNTUFUVVJFX0IiLCAiTUFUVVJFX0IiLCdDWUNMSU5HX01BVFVSRV9CJywgIkIxIiwgIlBMQVNNQV9CIiksCiAgICAgICAgICAgICAgICAgSUxDID0gYygiSUxDIiksCiAgICAgICAgICAgICAgICAgVF9jZWxsID0gYygiRE4oUCkvRE4oZWFybHkpIiwgIkROKFEpIiwgIkRQKFApIiwgIkRQKFEpIiwgImFiVChlbnRyeSkiLCAiQ0Q0K1QiLCAiQ0Q4K1QiLCAnQ0Q4YWEnLCJUcmVnIiwiTktUIiksCiAgICAgICAgICAgICAgICAgTksgPSBjKCJOSyIpKQoKY3RfY2xhc3NfZGYgPC0gaW1hcChjdF9vcmRlciwgfiBkYXRhLmZyYW1lKGNlbGx0eXBlPS54LCBjbGFzcz0ueSkpICU+JQogIGJpbmRfcm93cygpICU+JQogIG11dGF0ZShjZWxsdHlwZT1mYWN0b3IoY2VsbHR5cGUsIGxldmVscz11bmxpc3QoY3Rfb3JkZXIpKSkKCmZvciAobyBpbiBvcmdhbnMpewogIHBfZGYgPC0gZGVfZ2VuZXMgJT4lCiAgICBncm91cF9ieShnZW5lLCBvcmdhbikgJT4lCiAgICBtdXRhdGUobl9jZWxsdHlwZT1uKCkpICU+JQogICAgdW5ncm91cCgpICU+JQogICAgZmlsdGVyKG9yZ2FuID09IG8pICU+JQogICAgZmlsdGVyKG5fY2VsbHR5cGUgPiAzKSAlPiUKICAgICMgZmlsdGVyKGFicyhsb2dGb2xkQ2hhbmdlKSA+IDAuMSkgJT4lCiAgICByZW5hbWUob3JnX2V4cHIgPSBvKSAlPiUKICAgIGFycmFuZ2UoLSBvcmdfZXhwcikgJT4lCiAgICBtdXRhdGUoZ2VuZT1mYWN0b3IoZ2VuZSwgbGV2ZWxzPXVuaXF1ZShnZW5lKSkpICU+JQogICAgbGVmdF9qb2luKGN0X2NsYXNzX2RmKSAlPiUKICAgIG11dGF0ZShjZWxsdHlwZT1mYWN0b3IoY2VsbHR5cGUsIGxldmVscz11bmxpc3QoY3Rfb3JkZXIpKSkgCiAgbl9nZW5lcyA8LSBsZW5ndGgodW5pcXVlKHBfZGYkZ2VuZSkpCiAgcGxfaGVpZ2h0IDwtIGlmZWxzZShuX2dlbmVzID4gMTAsIDAuMypuX2dlbmVzLCAxKm5fZ2VuZXMpCiAgcCA8LSBwX2RmICU+JQogICAgZ2dwbG90KGFlcyhjZWxsdHlwZSwgZ2VuZSkpICsKICAgIGZhY2V0X2dyaWQob3JnYW5+Y2xhc3MsIHNjYWxlcz0iZnJlZV94Iiwgc3BhY2U9ImZyZWUiKSArCiAgICBnZW9tX3BvaW50KGFlcyhmaWxsPW9yZ19leHByLCBzaXplPWFicyhvcmdfZXhwcikpLCBzaGFwZT0yMSwgY29sb3I9J2JsYWNrJykgKwogICAgc2NhbGVfZmlsbF9ncmFkaWVudDIoaGlnaD0icmVkIiwgbG93PSJibHVlIikgKwogICAgc2NhbGVfY29sb3JfZ3JhZGllbnQyKGhpZ2g9InJlZCIsIGxvdz0iYmx1ZSIpICsKICAgIHRoZW1lX2dyYXkoYmFzZV9zaXplPTE0KSArCiAgICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9OTAsIGhqdXN0PTEsIHZqdXN0PTAuNSkpIAogIHByaW50KHApIAp9CgpgYGAKYGBge3IsIGZpZy5oZWlnaHQ9MTUsIGZpZy53aWR0aD0xMH0KbyA9ICJUSCIKCnBfZGYgPC0gZGVfZ2VuZXMgJT4lCiAgICBncm91cF9ieShnZW5lLCBvcmdhbikgJT4lCiAgICBtdXRhdGUobl9jZWxsdHlwZT1uKCkpICU+JQogICAgdW5ncm91cCgpICU+JQogICAgZmlsdGVyKG9yZ2FuID09IG8pICU+JQogICAgZmlsdGVyKG5fY2VsbHR5cGUgPiA1KSAlPiUKICAgICMgZmlsdGVyKGFicyhsb2dGb2xkQ2hhbmdlKSA+IDAuMSkgJT4lCiAgICByZW5hbWUob3JnX2V4cHIgPSBvKSAlPiUKICAgIGFycmFuZ2UoLSBvcmdfZXhwcikgJT4lCiAgICBtdXRhdGUoZ2VuZT1mYWN0b3IoZ2VuZSwgbGV2ZWxzPXVuaXF1ZShnZW5lKSkpICU+JQogICAgbGVmdF9qb2luKGN0X2NsYXNzX2RmKSAlPiUKICAgIG11dGF0ZShjZWxsdHlwZT1mYWN0b3IoY2VsbHR5cGUsIGxldmVscz11bmxpc3QoY3Rfb3JkZXIpKSkgCgpuX2dlbmVzIDwtIGxlbmd0aCh1bmlxdWUocF9kZiRnZW5lKSkKCnBfdG9wIDwtIG5fY2VsbHNbbl9jZWxscyRvcmdhbj09byxdICU+JQogICAgbXV0YXRlKG9yZ2FuPWZhY3RvcihvcmdhbiwgbGV2ZWxzPW9yZ2FucykpICU+JQogICAgZmlsdGVyKGNlbGx0eXBlICVpbiUgcF9kZiRjZWxsdHlwZSkgJT4lCiAgICBsZWZ0X2pvaW4oY3RfY2xhc3NfZGYpICU+JQogICAgbXV0YXRlKGNlbGx0eXBlPWZhY3RvcihjZWxsdHlwZSwgbGV2ZWxzPXVubGlzdChjdF9vcmRlcikpKSAgJT4lCiAgICBnZ3Bsb3QoYWVzKGNlbGx0eXBlLCBsb2cxMChuX2NlbGxzKSkpICsKICAgICAgZ2VvbV9jb2woKSArCiAgICB0aGVtZV9idyhiYXNlX3NpemU9MTQpICsKICBmYWNldF9ncmlkKG9yZ2FufmNsYXNzLCBzY2FsZXM9ImZyZWVfeCIsIHNwYWNlPSJmcmVlIikgKwogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSkgCgpwIDwtIHBfZGYgJT4lCiAgICBnZ3Bsb3QoYWVzKGNlbGx0eXBlLCBnZW5lKSkgKwogICAgZmFjZXRfZ3JpZChvcmdhbn5jbGFzcywgc2NhbGVzPSJmcmVlX3giLCBzcGFjZT0iZnJlZSIpICsKICAgIGdlb21fcG9pbnQoYWVzKGZpbGw9b3JnX2V4cHIsIHNpemU9YWJzKG9yZ19leHByKSksIHNoYXBlPTIxLCBjb2xvcj0nYmxhY2snKSArCiAgICBzY2FsZV9maWxsX2dyYWRpZW50MihoaWdoPSJyZWQiLCBsb3c9ImJsdWUiKSArCiAgICBzY2FsZV9jb2xvcl9ncmFkaWVudDIoaGlnaD0icmVkIiwgbG93PSJibHVlIikgKwogICAgdGhlbWVfZ3JheShiYXNlX3NpemU9MTQpICsKICAgIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT05MCwgaGp1c3Q9MSwgdmp1c3Q9MC41KSkgCgoocF90b3AgLyBwKSAgKyBwbG90X2xheW91dChoZWlnaHRzPWMoMSw1KSkKICAKICAKYGBgCgpgYGB7ciwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KCm1hcmtlcl9nZW5lc191cCA8LSBwX2RmJGdlbmUKCmVtX3VwIDwtIGVucmljaGVyKG1hcmtlcl9nZW5lc191cCwgVEVSTTJHRU5FPW1fdDJnLCBwQWRqdXN0TWV0aG9kID0gImZkciIsIAogICAgICAgICAgICAgICAgICB1bml2ZXJzZSA9IGdlbmVzJEdlbmVOYW1lCiAgICAgICAgICAgICAgICAgICkKCmVtX3Jlc191cCA8LSBlbV91cEByZXN1bHRbZW1fdXBAcmVzdWx0JHF2YWx1ZSA8IDAuMSxdICU+JQogIGRwbHlyOjpzZWxlY3QoLSBjKERlc2NyaXB0aW9uKSkKCmVtX3Jlc191cFtjKCdJRCcsIkdlbmVSYXRpbyIsImdlbmVJRCIpXQojIGVtX3Jlc19kb3duW2MoJ0lEJywiR2VuZVJhdGlvIiwiZ2VuZUlEIildCmBgYAoKPCEtLSBgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTZ9IC0tPgo8IS0tIGRlX2dlbmVzICU+JSAtLT4KPCEtLSAgIGZpbHRlcihvcmdhbj09IkxJIikgJT4lIC0tPgo8IS0tICAgc2VsZWN0KGNlbGx0eXBlLCBnZW5lLCBsb2dGb2xkQ2hhbmdlKSAlPiUgLS0+CjwhLS0gICBwaXZvdF93aWRlcihpZF9jb2xzPWdlbmUsIG5hbWVzX2Zyb209Y2VsbHR5cGUsIHZhbHVlc19mcm9tPSJsb2dGb2xkQ2hhbmdlIiwgdmFsdWVzX2ZpbGw9MCkgJT4lIC0tPgo8IS0tICAgY29sdW1uX3RvX3Jvd25hbWVzKCJnZW5lIikgJT4lIC0tPgo8IS0tICAgYXMubWF0cml4KCkgJT4lIC0tPgo8IS0tICAgcGhlYXRtYXA6OnBoZWF0bWFwKCkgLS0+CjwhLS0gYGBgIC0tPgoKCmBgYHtyLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9N30KcGxvdF9jZWxsdHlwZV9vcmdfZGUoZGVfZ2VuZXMsIG9yZyA9ICJCTSIsIGN0PSJFTFAvUFJFX1BST19CIiwgbWluRkMgPSAwLjUpCnBsb3RfY2VsbHR5cGVfb3JnX2RlKGRlX2dlbmVzLCBvcmcgPSAiTEkiLCBjdD0iRUxQL1BSRV9QUk9fQiIsIG1pbkZDID0gMC41KQpgYGAKCmBgYHtyfQpkZV9nZW5lcyAlPiUKICBncm91cF9ieShvcmdhbiwgY2VsbHR5cGUpICU+JQogIGRvKGRhdGEuZnJhbWUoQ29yPXQoY29yKC5bLG9yZ2Fuc10sdXNlID0gKSksIG9yZ2FuMj1vcmdhbnMpKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IHN0cl9jKCdDb3IuJywgb3JnYW5zKSwgbmFtZXNfdG89Im9yZ2FuMSIpICU+JQogIGZpbHRlcihjZWxsdHlwZT09IkVMUC9QUkVfUFJPX0IiICYgb3JnYW49PSJCTSIpICU+JQogIGdncGxvdChhZXMob3JnYW4xLCBvcmdhbjIpKSArCiAgZ2VvbV90aWxlKGFlcyhmaWxsPXZhbHVlKSkgKwogIGZhY2V0X3dyYXAoIm9yZ2FuIikgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKGhpZ2g9InJlZCIsIGxvdz0iYmx1ZSIpCiAgCiAgCmBgYApgYGB7ciwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTd9Cm9yZyA8LSAiQk0iCiMgY21hdCA8LSAKICBkZV9nZW5lcyAlPiUKICAjIGZpbHRlcihvcmdhbiAlaW4lIG9yZykgJT4lCiAgZ3JvdXBfYnkob3JnYW4sIGNlbGx0eXBlKSAlPiUKICBmaWx0ZXIobigpID4gMjApICU+JQogICAgZmlsdGVyKGNlbGx0eXBlPT0iUFJPX0IiKQogIGRvKGRhdGEuZnJhbWUoQ29yPXQoY29yKC5bLG9yZ2Fuc10sdXNlID0gKSksIG9yZ2FuMiA9IG9yZ2FucykpICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gc3RyX2MoJ0Nvci4nLCBvcmdhbnMpLCBuYW1lc190bz0ib3JnYW4xIikgJT4lCiAgbXV0YXRlKG9yZ2FuMT1zdHJfcmVtb3ZlKG9yZ2FuMSwgIkNvci4iKSkgJT4lCiAgZmlsdGVyKG9yZ2FuMj09b3JnYW4pICU+JQogIGZpbHRlcihvcmdhbjEgIT0gb3JnKSAlPiUKICBnZ3Bsb3QoYWVzKG9yZ2FuMSwgY2VsbHR5cGUsIGZpbGw9dmFsdWUpKSArIAogIGdlb21fdGlsZSgpICsKICAgIGZhY2V0X2dyaWQob3JnYW4yfi4sIHNjYWxlcz0iZnJlZSIsIHNwYWNlPSJmcmVlIikgKwogICAgc2NhbGVfZmlsbF9ncmFkaWVudDIoaGlnaD0icmVkIiwgbG93PSJibHVlIikKCmBgYApgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTd9CnBsb3RfY2VsbHR5cGVfb3JnX2RlKGRlX2dlbmVzLCBvcmcgPSAiVEgiLCBjdD0iUHJvZ2VuaXRvciIsIG1pbkZDID0gMSkKcGxvdF9jZWxsdHlwZV9vcmdfZGUoZGVfZ2VuZXMsIG9yZyA9ICJUSCIsIGN0PSJETihQKS9ETihlYXJseSkiKQpgYGAKYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD03fQpwbG90X2NlbGx0eXBlX29yZ19kZShkZV9nZW5lcywgb3JnID0gIlRIIiwgY3Q9IkIxIikKcGxvdF9jZWxsdHlwZV9vcmdfZGUoZGVfZ2VuZXMsIG9yZyA9ICJMSSIsIGN0PSJCMSIpCnBsb3RfY2VsbHR5cGVfb3JnX2RlKGRlX2dlbmVzLCBvcmcgPSAiU1AiLCBjdD0iQjEiKQpwbG90X2NlbGx0eXBlX29yZ19kZShkZV9nZW5lcywgb3JnID0gIkJNIiwgY3Q9IkIxIikKYGBgCgpgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTd9CnBsb3RfY2VsbHR5cGVfb3JnX2RlKGRlX2dlbmVzLCBvcmcgPSAiU1AiLCBjdD0iTktUIikKcGxvdF9jZWxsdHlwZV9vcmdfZGUoZGVfZ2VuZXMsIG9yZyA9ICJTUCIsIGN0PSJOSyIpCmBgYAoKIyMgVmlzdWFsaXplIHJlc3VsdHMKCmBgYHtyfQpsaWJyYXJ5KHBhdGNod29yaykKIyMgVmlzdWFsaXplIGdlbmUgZXhwcmVzc2lvbiBmb3Igb25lIGNlbGx0eXBlIC0gb3JnYW4gcGFpcgpwbG90X2NlbGx0eXBlX29yZ19kZSA8LSBmdW5jdGlvbihkZV9nZW5lcywgY3QsIG9yZywgbWluRkM9MCl7CiAgZGYgPC0gZGVfZ2VuZXMgJT4lCiAgZmlsdGVyKGNlbGx0eXBlPT1jdCAmIG9yZ2FuPT1vcmcpIAogIAogIGlmIChtaW5GQyA+IDApIHsKICAgIGRmIDwtIGZpbHRlcihkZiwgYWJzKGxvZ0ZvbGRDaGFuZ2UpID4gbWluRkMpCiAgfQogIAogIHBfdG9wIDwtIG5fY2VsbHNbbl9jZWxscyRjZWxsdHlwZT09Y3QsXSAlPiUKICAgIG11dGF0ZShvcmdhbj1mYWN0b3Iob3JnYW4sIGxldmVscz1vcmdhbnMpKSAlPiUKICAgIGdncGxvdChhZXMob3JnYW4sIGxvZzEwKG5fY2VsbHMpKSkgKwogICAgICBnZW9tX2NvbCgpICsKICAgIGdndGl0bGUocGFzdGUoY3QsIG9yZywgc2VwPSctJykpICsKICAgIHRoZW1lX2J3KGJhc2Vfc2l6ZT0xNCkgCiAgcF9ib3QgPC0gZGYgJT4lCiAgYXJyYW5nZShsb2dGb2xkQ2hhbmdlKSAlPiUKICBtdXRhdGUoZ2VuZSA9IGZhY3RvcihnZW5lLCBsZXZlbHM9Z2VuZSkpICU+JQogIHBpdm90X2xvbmdlcihjb2xzPW9yZ2FucywgbmFtZXNfdG89Im9yZ2FuX2V4cHIiLCB2YWx1ZXNfdG89ImV4cHIiKSAlPiUKICAgIG11dGF0ZShvcmdhbl9leHByPWZhY3Rvcihvcmdhbl9leHByLCBsZXZlbHM9b3JnYW5zKSkgJT4lCiAgZ2dwbG90KGFlcyhvcmdhbl9leHByLCBnZW5lKSkgKyBnZW9tX3RpbGUoYWVzKGZpbGw9ZXhwcikpICsKICBzY2FsZV9maWxsX2dyYWRpZW50MihoaWdoID0gInJlZCIsIGxvdz0iYmx1ZSIsIG5hbWU9IkF2Zy4gRXhwci4iKSArCiAgICB0aGVtZV9idyhiYXNlX3NpemU9MTQpIAogIChwX3RvcCAvIHBfYm90KSArIHBsb3RfbGF5b3V0KGhlaWdodHM9YygxLDgpKQogIAp9CgojIyBWaXN1YWxpemUgb25lIGdlbmUgKHdoZXJlIHdhcyB0aGF0IGdlbmUgREU/KQpwbG90X2dlbmVfZGUgPC0gZnVuY3Rpb24oZGVfZ2VuZXMsIGcpewogIGRlX2dlbmVzICU+JQogICAgZmlsdGVyKGdlbmU9PWcpICU+JQogICAgZ2dwbG90KGFlcyhvcmdhbiwgY2VsbHR5cGUsIGZpbGw9bG9nRm9sZENoYW5nZSkpICsKICAgIGdlb21fdGlsZSgpICsKICAgIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKGhpZ2ggPSAicmVkIiwgbG93PSJibHVlIiwgbmFtZT0iTG9nRkMiKSArCiAgICB0aGVtZV9idyhiYXNlX3NpemU9MTYpICsKICAgIGdndGl0bGUoZykKICB9CgpwbG90X2dlbmVfZGUoZGVfZ2VuZXMsICJOS0c3IikKcGxvdF9nZW5lX2RlKGRlX2dlbmVzLCAiQ0Q0MCIpCgojIyBCMSBjZWxscwpCMV9nZW5lcyA8LSBjKCJNU0E0MSIsICJDRDI0IiwgIkNENDAiLCAiSUdITSIsICJJR0hEIiwiRkNFUjIiKQpmb3IgKGcgaW4gQjFfZ2VuZXMpIHsgcHJpbnQocGxvdF9nZW5lX2RlKGRlX2dlbmVzLCBnPWcpKSB9CgpwbG90X2dlbmVfZGUoZGVfZ2VuZXMsICJSR1MxIikKcGxvdF9nZW5lX2RlKGRlX2dlbmVzLCAiSUwzMiIpCnBsb3RfZ2VuZV9kZShkZV9nZW5lcywgIkMxUUMiKQojIyBOSyBtYXJrZXJzCnBsb3RfZ2VuZV9kZShkZV9nZW5lcywgIktMUkIxIikKcGxvdF9nZW5lX2RlKGRlX2dlbmVzLCAiQ0QzRyIpCnBsb3RfZ2VuZV9kZShkZV9nZW5lcywgIk1TNEExIikKCiMjIEVUUCBtYXJrZXJzCmxhcHBseShjKCdBQ1kzJywgJ0tDTksxNycsICdOUFRYMicpLCBwbG90X2dlbmVfZGUsIGRlX2dlbmVzPWRlX2dlbmVzKQpgYGAKCjwhLS0gYGBge3IsIGZpZy5oZWlnaHQ9NTAsIGZpZy53aWR0aD03fSAtLT4KPCEtLSB5c19scyA8LSBsYXBwbHkoY3RfZGYkY2VsbHR5cGUsIGZ1bmN0aW9uKGN0KSBwbG90X2NlbGx0eXBlX29yZ19kZShkZV9nZW5lcywgb3JnPSJZUyIsIGN0PWN0KSArIGNvb3JkX2ZpeGVkKCkpIC0tPgo8IS0tIHdyYXBfcGxvdHMoeXNfbHMsIG5jb2w9MSwgZ3VpZGVzPSJjb2xsZWN0IikgLS0+Cgo8IS0tIGRlX2dlbmVzICU+JSAtLT4KPCEtLSAgIGZpbHRlcihvcmdhbj09IllTIikgJT4lIC0tPgo8IS0tICAgIyBhcnJhbmdlKGxvZ0ZvbGRDaGFuZ2UpICU+JSAtLT4KPCEtLSAgICMgbXV0YXRlKGdlbmUgPSBmYWN0b3IoZ2VuZSwgbGV2ZWxzPWdlbmUpKSAlPiUgLS0+CjwhLS0gICBwaXZvdF9sb25nZXIoY29scz1vcmdhbnMsIG5hbWVzX3RvPSJvcmdhbl9leHByIiwgdmFsdWVzX3RvPSJleHByIikgJT4lIC0tPgo8IS0tICAgZ2dwbG90KGFlcyhvcmdhbl9leHByLCBnZW5lKSkgKyAgLS0+CjwhLS0gICBnZW9tX3RpbGUoYWVzKGZpbGw9ZXhwcikpICsgLS0+CjwhLS0gICBmYWNldF9ncmlkKGNlbGx0eXBlfi4sIHNwYWNlPSJmcmVlIiwgc2NhbGVzPSJmcmVlIikgKyAtLT4KPCEtLSAgIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKGhpZ2ggPSAicmVkIiwgbG93PSJibHVlIiwgbmFtZT0iQXZnLiBFeHByLiIpICsgLS0+CjwhLS0gICB0aGVtZShzdHJpcC50ZXh0LnkgPSBlbGVtZW50X3RleHQoYW5nbGU9MCkpIC0tPgo8IS0tICAgIyBnZ3RpdGxlKHBhc3RlKGN0LCBvcmcsIHNlcD0nLScpKSAtLT4KPCEtLSBgYGAgLS0+CgpgYGB7cn0KZGVfZ2VuZXMgJT4lCiAgZmlsdGVyKGNlbGx0eXBlPT0iQ0Q4YWEiKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IG9yZ2FucywgbmFtZXNfdG89ImV4cHJfb3JnYW5zIiwgdmFsdWVzX3RvPSJleHByIikgJT4lCiAgZ3JvdXBfYnkob3JnYW4sIGV4cHJfb3JnYW5zKSAlPiUKICBzdW1tYXJpc2Uoc2lnbmF0dXJlPW1lYW4oYWJzKGV4cHIpKSkgJT4lCiAgZ2dwbG90KGFlcyhleHByX29yZ2Fucywgc2lnbmF0dXJlLCBjb2xvcj1vcmdhbikpICsgZ2VvbV9wb2ludCgpCiAgCmBgYAoKYGBge3IsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0xM30KCnBsb3RfY2VsbHR5cGVfb3JnX2RlKGRlX2dlbmVzLCAiTUFUVVJFX0IiLCAiWVMiKSArCiAgY29vcmRfZml4ZWQoKQpwbG90X2NlbGx0eXBlX29yZ19kZShkZV9nZW5lcywgIklMQyIsICJMSSIsIG1pbkZDID0gMC41KSArCiAgY29vcmRfZml4ZWQoKQoKcGxvdF9jZWxsdHlwZV9vcmdfZGUoZGVfZ2VuZXMsICJOS1QiLCAiVEgiLCBtaW5GQyA9IDAuNSkgKwogIGNvb3JkX2ZpeGVkKCkKCnBsb3RfY2VsbHR5cGVfb3JnX2RlKGRlX2dlbmVzLCAiUHJvZ2VuaXRvciIsICJUSCIsIG1pbkZDID0gMC41KSArCiAgY29vcmRfZml4ZWQoKQpwbG90X2NlbGx0eXBlX29yZ19kZShkZV9nZW5lcywgIkNENCtUIiwgIlRIIiwgbWluRkMgPSAwLjUpICsKICBjb29yZF9maXhlZCgpICsKcGxvdF9jZWxsdHlwZV9vcmdfZGUoZGVfZ2VuZXMsICJDRDgrVCIsICJUSCIsIG1pbkZDID0gMC41KSArCiAgY29vcmRfZml4ZWQoKQpgYGAKYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD04fQpwbG90X2NlbGx0eXBlX29yZ19kZShkZV9nZW5lcywgIkNENCtUIiwgIk1MTiIsIG1pbkZDID0gMC4zKSArCiAgY29vcmRfZml4ZWQoKSArCnBsb3RfY2VsbHR5cGVfb3JnX2RlKGRlX2dlbmVzLCAiQ0Q4K1QiLCAiTUxOIiwgbWluRkMgPSAwLjMpICsKICBjb29yZF9maXhlZCgpIAogIApgYGAKYGBge3IsIGZpZy5oZWlnaHQ9N30KcGxvdF9jZWxsdHlwZV9vcmdfZGUoZGVfZ2VuZXMsICJDRDQrVCIsICJTSyIsIG1pbkZDID0gMC4yKSArCiAgY29vcmRfZml4ZWQoKSArCnBsb3RfY2VsbHR5cGVfb3JnX2RlKGRlX2dlbmVzLCAiQ0Q0K1QiLCAiR1UiLCBtaW5GQyA9IDAuMikgKwogIGNvb3JkX2ZpeGVkKCkgKwpwbG90X2NlbGx0eXBlX29yZ19kZShkZV9nZW5lcywgIkNENCtUIiwgIktJIiwgbWluRkMgPSAwLjIpICsKICBjb29yZF9maXhlZCgpIApwbG90X2NlbGx0eXBlX29yZ19kZShkZV9nZW5lcywgIkNEOCtUIiwgIlNQIiwgbWluRkMgPSAwLjIpICsKICBjb29yZF9maXhlZCgpIAoKYGBgCmBgYHtyfQpwbG90X2NlbGx0eXBlX29yZ19kZShkZV9nZW5lcywgIk1BVFVSRV9CIiwgIlRIIiwgbWluRkMgPSAwLjgpICsKICBjb29yZF9maXhlZCgpCmBgYAoKCiMjIFJlYWQgYWxsIHJlc3VsdHMKCmBgYHtyfQpsaWJyYXJ5KGdsdWUpCmxvYWQoJy9uZnMvdGVhbTIwNS9uazUvVGVhbTIwNS9lZDYvREUvZGVfY2VsbHR5cGUuUmJpbicpCmxvYWQoJy9uZnMvdGVhbTIwNS9uazUvVGVhbTIwNS9lZDYvREUvZGVfb3JnYW4uUmJpbicpCgpkaW0oZGVfb3JnYW4kYmV0YQpgYGAKYGBge3J9Cm9yZyA8LSAiTEkiCmN0IDwtICJNQVRVUkVfQiIKCmN0X2lkIDwtIGN0X2RmJElEW2N0X2RmJGNlbGx0eXBlPT1jdF0KCmRlX2N0X29yZyA8LSByZWFkUkRTKGdsdWUoJy9uZnMvdGVhbTIwNS9uazUvVGVhbTIwNS9lZDYvREUvZGVfe2N0X2lkfS5SRFMnKSkKc2lnbmlmX2dlbmVzIDwtIGRlX2dlbmVzJGdlbmVbZGVfZ2VuZXMkb3JnYW49PW9yZyAmIGRlX2dlbmVzJGNlbGx0eXBlPT1jdF0KCmRhdGEuZnJhbWUoY3Q9ZGVfb3JnYW4kYmV0YVssb3JnXSwgY3Rfb3JnID0gZGVfY3Rfb3JnJGJldGFbLGdsdWUoIntjdH06e29yZ30iKV0sIGdlbmU9cm93bmFtZXMoZGVfY2VsbHR5cGUkYmV0YSkpICU+JQogIG11dGF0ZShpc19zaWduaWYgPSBnZW5lICVpbiUgc2lnbmlmX2dlbmVzKSAlPiUKICBnZ3Bsb3QoYWVzKGN0LCBjdF9vcmcpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3BvaW50KGRhdGE9IC4gJT4lIGZpbHRlcihpc19zaWduaWYpLCBjb2xvcj0icmVkIikgKwogIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChkYXRhPSAuICU+JSBmaWx0ZXIoaXNfc2lnbmlmKSwgY29sb3I9InJlZCIsIGFlcyhsYWJlbD1nZW5lKSkgKwogIHhsYWIob3JnKSArIHlsYWIoZ2x1ZSgie2N0fTp7b3JnfSIpKQpgYGAKCgpWUyBtYXJrZXIgZ2VuZXMKCmBgYHtyfQpvcmcgPC0gIkxJIgpjdCA8LSAiUFJPX0IiCgpwbG90X2NvZWZmc19tYXJrZXJzIDwtIGZ1bmN0aW9uKGN0LCBvcmcpewogIGN0X2lkIDwtIGN0X2RmJElEW2N0X2RmJGNlbGx0eXBlPT1jdF0KICBkZV9jdF9vcmcgPC0gcmVhZFJEUyhnbHVlKCcvbmZzL3RlYW0yMDUvbms1L1RlYW0yMDUvZWQ2L0RFL2RlX3tjdF9pZH0uUkRTJykpCiAgc2lnbmlmX2dlbmVzIDwtIGRlX2dlbmVzJGdlbmVbZGVfZ2VuZXMkb3JnYW49PW9yZyAmIGRlX2dlbmVzJGNlbGx0eXBlPT1jdF0KICAKICBkYXRhLmZyYW1lKGN0PWRlX2NlbGx0eXBlJGJldGFbLGN0XSwgY3Rfb3JnID0gZGVfY3Rfb3JnJGJldGFbLGdsdWUoIntjdH06e29yZ30iKV0sIGdlbmU9cm93bmFtZXMoZGVfY2VsbHR5cGUkYmV0YSkpICU+JQogICAgbXV0YXRlKGlzX3NpZ25pZiA9IGdlbmUgJWluJSBzaWduaWZfZ2VuZXMpICU+JQogICAgZ2dwbG90KGFlcyhjdCwgY3Rfb3JnKSkgKwogICAgZ2VvbV9wb2ludCgpICsKICAgIGdlb21fcG9pbnQoZGF0YT0gLiAlPiUgZmlsdGVyKGlzX3NpZ25pZiksIGNvbG9yPSJyZWQiKSArCiAgICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoZGF0YT0gLiAlPiUgZmlsdGVyKGlzX3NpZ25pZiksIGNvbG9yPSJyZWQiLCBhZXMobGFiZWw9Z2VuZSkpICsKICAgIHhsYWIoY3QpICsgeWxhYihnbHVlKCJ7Y3R9Ontvcmd9IikpCiAgCn0KCnBsb3RfY29lZmZzX21hcmtlcnMoIk5LVCIsICJTUCIpCnBsb3RfY29lZmZzX21hcmtlcnMoIk1BVFVSRV9CIiwgIlNQIikKYGBgCgpgYGB7cn0Kb3JnMSA8LSAiQk0iCm9yZzIgPC0gIkxJIgpjdCA8LSAiUFJPX0IiCgpjdF9pZCA8LSBjdF9kZiRJRFtjdF9kZiRjZWxsdHlwZT09Y3RdCgpkZV9jdF9vcmcgPC0gcmVhZFJEUyhnbHVlKCcvbmZzL3RlYW0yMDUvbms1L1RlYW0yMDUvZWQ2L0RFL2RlX3tjdF9pZH0uUkRTJykpCnNpZ25pZl9nZW5lcyA8LSBkZV9nZW5lcyRnZW5lW2RlX2dlbmVzJG9yZ2FuPT1vcmcgJiBkZV9nZW5lcyRjZWxsdHlwZT09Y3RdCgpkYXRhLmZyYW1lKGN0PWRlX2N0X29yZyRiZXRhWyxnbHVlKCJ7Y3R9OntvcmcxfSIpXSwgY3Rfb3JnID0gZGVfY3Rfb3JnJGJldGFbLGdsdWUoIntjdH06e29yZzJ9IildLCBnZW5lPXJvd25hbWVzKGRlX2NlbGx0eXBlJGJldGEpKSAlPiUKICBtdXRhdGUoaXNfc2lnbmlmID0gZ2VuZSAlaW4lIHNpZ25pZl9nZW5lcykgJT4lCiAgZ2dwbG90KGFlcyhjdCwgY3Rfb3JnKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9wb2ludChkYXRhPSAuICU+JSBmaWx0ZXIoaXNfc2lnbmlmKSwgY29sb3I9InJlZCIpICsKICBnZW9tX3RleHQoZGF0YT0gLiAlPiUgZmlsdGVyKGlzX3NpZ25pZiksIGNvbG9yPSJyZWQiLCBhZXMobGFiZWw9Z2VuZSkpICsKICB4bGFiKGdsdWUoIntjdH06e29yZzF9IikpICsgeWxhYihnbHVlKCJ7Y3R9OntvcmcyfSIpKQpgYGAKCgpgYGB7cn0KbGlicmFyeShIbWlzYykKCmZvciAoY3QgaW4gY3RfZGYkY2VsbHR5cGUpewogIGN0X2lkIDwtIGN0X2RmJElEW2N0X2RmJGNlbGx0eXBlPT1jdF0KICBkZV9jdF9vcmcgPC0gcmVhZFJEUyhnbHVlKCcvbmZzL3RlYW0yMDUvbms1L1RlYW0yMDUvZWQ2L0RFL2RlX3tjdF9pZH0uUkRTJykpCiAgCiAgY29ybWF0IDwtIHJjb3JyKGRlX2N0X29yZyRiZXRhKSAgCiAgY29ybWF0JHJbY29ybWF0JFAgPiAwLjAxXSA8LSAwCiAgY29ycnBsb3Q6OmNvcnJwbG90KGNvcm1hdCRyLG1ldGhvZCA9ICdjb2xvcicgLCBkaWFnID0gVFJVRSwgYWRkQ29lZi5jb2wgPSAiZ3JleSIsIGhjbHVzdC5tZXRob2QgPSAid2FyZCIpCiAgCn0KYGBgCmBgYHtyLCBmaWcuaGVpZ2h0PTExLCBmaWcud2lkdGg9MTF9CmRlX2dlbmVzICU+JQogIGdyb3VwX2J5KG9yZ2FuLCBjZWxsdHlwZSkgJT4lCiAgc3VtbWFyaXNlKG5fZGVfZ2VuZXMgPSBuKCkpICU+JQogIGdncGxvdChhZXMob3JnYW4sIG5fZGVfZ2VuZXMpKSArCiAgZ2VvbV9jb2woKSArCiAgY29vcmRfZmxpcCgpICsKICB0aGVtZV9idyhiYXNlX3NpemUgPSAxNCkgKwogIGZhY2V0X3dyYXAoY2VsbHR5cGV+Liwgc2NhbGVzPSJmcmVlX3giKQpgYGAKCgojIyMgQ29tcGFyZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIGNvZWZmaWNpZW50cyBhbmQgc2lnbmF0dXJlcwppLmUuIGNlbGxzIHdoZXJlIHRoZSBnZW5lIHNldCBpcyBleHByZXNzZWQKYGBge3J9CiMjIExvYWQgc2lnbmF0dXJlcwpzaWdfbWF0IDwtIHJlYWRfY3N2KCIvbmZzL3RlYW0yMDUvZWQ2L2RhdGEvRmV0YWxfaW1tdW5lL0xNTV9seW1waF9zaWduYXR1cmVzX3Njb3Jlcy5jc3YiKSAlPiUKICBjb2x1bW5fdG9fcm93bmFtZXMoIlgxIikgCgpjb3Jfc2lnIDwtIGNvcihzaWdfbWF0KQpgYGAKCgpgYGB7cn0KYmV0YV9scyA8LSBsYXBwbHkoY3RfZGYkY2VsbHR5cGUsIGZ1bmN0aW9uKGN0KXsKICBjdF9pZCA8LSBjdF9kZiRJRFtjdF9kZiRjZWxsdHlwZT09Y3RdCiAgZGVfY3Rfb3JnIDwtIHJlYWRSRFMoZ2x1ZSgnL25mcy90ZWFtMjA1L25rNS9UZWFtMjA1L2VkNi9ERS9kZV97Y3RfaWR9LlJEUycpKQogIGRlX2N0X29yZyRiZXRhCiAgfSkKCmJldGFfbWF0IDwtIHJlZHVjZShiZXRhX2xzLCBjYmluZCkgCgpiZXRhX2RmIDwtIGRhdGEuZnJhbWUoYmV0YV9tYXQpCmNvbG5hbWVzKGJldGFfZGYpIDwtIGNvbG5hbWVzKGJldGFfbWF0KQp3cml0ZV9jc3YoYmV0YV9kZiwgIi9ob21lL2pvdnlhbi9tb3VudC9nZHJpdmUvUGFuX2ZldGFsL3NpZ25pZmljYW50X2dlbmVzL0xNTV9OYXRzdWhpa29fcmVzdWx0cy9kZV9seW1waG9pZF9sdHNyMC45LmJldGEuY3N2IikKCmNvcl9iZXRhIDwtIGNvcihiZXRhX21hdCkKYGBgCmBgYHtyLCBmaWcuaGVpZ2h0PTE1LCBmaWcud2lkdGg9MTV9CmxpYnJhcnkoUkNvbG9yQnJld2VyKQoKa2VlcF9jb2xzIDwtIGRlX2dlbmVzICU+JQogIGdyb3VwX2J5KGNlbGx0eXBlLCBvcmdhbikgJT4lCiAgc3VtbWFyaXNlKG49bigpKSAlPiUKICBmaWx0ZXIobiA+IDMwKSAlPiUKICBtdXRhdGUoY29scyA9IHN0cl9jKGNlbGx0eXBlLCI6Iiwgb3JnYW4pKSAlPiUKICBwdWxsKGNvbHMpCgphbm5vX2RmIDwtIGRhdGEuZnJhbWUobmFtZXM9Y29sbmFtZXMoY29yX2JldGEpKSAgJT4lCiAgbXV0YXRlKGNvbHM9bmFtZXMpICU+JQogIHNlcGFyYXRlKG5hbWVzLCBpbnRvPWMoImN0IiwgIm9yZ2FuIiksIHNlcD0iOiIpICU+JQogIHNlbGVjdChvcmdhbiwgY29scykgJT4lCiAgY29sdW1uX3RvX3Jvd25hbWVzKCdjb2xzJykKCnBhbGV0dGUgPC0gYnJld2VyLnBhbChsZW5ndGgob3JnYW5zKSwgbmFtZT0iU3BlY3RyYWwiKQoKcGhlYXRtYXA6OnBoZWF0bWFwKGNvcl9iZXRhW2tlZXBfY29scyxrZWVwX2NvbHNdLCBhbm5vdGF0aW9uX2NvbCA9IGFubm9fZGYsIGFubm90YXRpb25fcm93ID0gYW5ub19kZiwgYW5ub3RhdGlvbl9jb2xvcnMgPSBsaXN0KG9yZ2FuPXNldE5hbWVzKHBhbGV0dGUsIG9yZ2FucykpKQpgYGAKCmBgYHtyfQpzd2l0Y2hfb3JnX2N0IDwtIGZ1bmN0aW9uKHMpewogIHN0cl9scyA8LSBzdHJfc3BsaXQocywgIl8iKQogIHNhcHBseShzdHJfbHMsIGZ1bmN0aW9uKHgpIHN0cl9jKHhbMl0sICJfIiwgeFsxXSkpCn0KCmxvbmdfY29yX2JldGEgPC0gbWVsdChjb3JfYmV0YSkgJT4lCiAgbXV0YXRlKFZhcjEgPSBzdHJfcmVwbGFjZShWYXIxLCAiOiIsICJfIiksIFZhcjIgPSBzdHJfcmVwbGFjZShWYXIyLCAiOiIsICJfIikgKSAlPiUKICBtdXRhdGUoVmFyMSA9IHN3aXRjaF9vcmdfY3QoVmFyMSksIFZhcjIgPSBzd2l0Y2hfb3JnX2N0KFZhcjIpKSAlPiUKICByZW5hbWUoY29yX2JldGEgPSB2YWx1ZSkKCmxvbmdfY29yX3NpZyA8LSBtZWx0KGNvcl9zaWcpICU+JQogIG11dGF0ZShWYXIxID0gc3RyX3JlbW92ZShWYXIxLCAic2lnX3Njb3JlXyIpLCBWYXIyID0gc3RyX3JlbW92ZShWYXIyLCAic2lnX3Njb3JlXyIpICkgJT4lCiAgcmVuYW1lKGNvcl9zaWcgPSB2YWx1ZSkKCmxvbmdfY29yIDwtIGxlZnRfam9pbihsb25nX2Nvcl9zaWcsIGxvbmdfY29yX2JldGEpIApgYGAKYGBge3J9CmxvbmdfY29yICU+JQogIGZpbHRlcihWYXIxIT1WYXIyKSAlPiUKICBtdXRhdGUobGFiZWw9aWZlbHNlKChhYnMoY29yX3NpZykgPiAwLjMpIHwgKGFicyhjb3JfYmV0YSkgPiAwLjMpLCBzdHJfYyhWYXIxLCJfIixWYXIyKSwgTkEpKSAlPiUKICBtdXRhdGUoY2xhc3MgPSBjYXNlX3doZW4oc3RyX3JlbW92ZShWYXIxLCAiXy4rIikgPT0gc3RyX3JlbW92ZShWYXIyLCAiXy4rIikgfiAic2FtZSBvcmdhbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cl9yZW1vdmUoVmFyMSwgIi4rXyIpID09IHN0cl9yZW1vdmUoVmFyMiwgIi4rXyIpIH4gInNhbWUgY2VsbCB0eXBlIikpICU+JQogIGdncGxvdChhZXMoY29yX3NpZywgY29yX2JldGEsIGNvbG9yPWNsYXNzKSkgKwogIGdlb21fcG9pbnQoc2l6ZT0xKSArCiAgc3RhdF9jb3IoKSArCiAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbD1sYWJlbCkpICsKICB4bGFiKCJTaWduYXR1cmUgY29ycmVsYXRpb24gKHNhbWUgY2VsbHMpIikgKwogIHlsYWIoIkJldGEgY29ycmVsYXRpb24gKHNhbWUgZ2VuZXMpIikgCmBgYApgYGB7ciwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTV9CnBsb3RfY2VsbHR5cGVfb3JnX2RlKGRlX2dlbmVzLCAiUHJvZ2VuaXRvciIsICJUSCIsIG1pbkZDID0gMSkgCnBsb3RfY2VsbHR5cGVfb3JnX2RlKGRlX2dlbmVzLCAiUFJPX0IiLCAiQk0iKSAKYGBgCgpgYGB7cn0KbG9hZCgnL25mcy90ZWFtMjA1L25rNS9UZWFtMjA1L2VkNi9mbGFnXzAuMDUuUmJpbicpCmdlbmVzIDwtIHJlYWQuY3N2KCIvbmZzL3RlYW0yMDUvbms1L1RlYW0yMDUvZWQ2L2dlbmUuY3N2Lmd6Iixhcy5pcz1UKQp3cml0ZV9jc3YoZ2VuZXNbZmxhZyxdWyJHZW5lTmFtZSJdLCAiL2hvbWUvam92eWFuL21vdW50L2dkcml2ZS9QYW5fZmV0YWwvc2lnbmlmaWNhbnRfZ2VuZXMvTE1NX05hdHN1aGlrb19yZXN1bHRzL2RlX2x5bXBob2lkX2x0c3IwLjkuZ2VuZXMuY3N2IikKYGBgCgoK